package com.terra.ns.imp.common.redis.utils

import cn.hutool.extra.spring.SpringUtil
import org.redisson.api.RAtomicLong
import org.redisson.api.RBatch
import org.redisson.api.RTopic
import org.redisson.api.RedissonClient
import java.time.Duration
import java.util.function.Consumer
import java.util.stream.Collectors

/**
@author qins
@date 2023/5/30
@desc 统一使用redisson客户端操作redis
 */
object RedisUtils {

    // 通过CLIENT 可获取HashMap，Set, List等的操作
    val CLIENT : RedissonClient = SpringUtil.getBean(RedissonClient::class.java)

    /**
     * 检查缓存是否存在
     * @param key 缓存的键值
     */
    fun hasKey(key: String): Boolean {
        return CLIENT.getBucket<Any>(key).isExists
    }

    /**
     * 缓存基本的对象，Int、String、实体类等
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    fun <T> set(key: String, value: T) {
        val bucket = CLIENT.getBucket<T>(key)
        bucket.set(value)
    }

    /**
     * 缓存基本的对象，当key不存在的时候
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    fun <T> setIfAbsent(key: String, value: T, duration: Duration): Boolean {
        val bucket = CLIENT.getBucket<T>(key)
        return bucket.setIfAbsent(value, duration)
    }

    /**
     * 缓存基本的对象，当key不存在的时候
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    fun <T> setIfAbsent(key: String, value: T): Boolean {
        val bucket = CLIENT.getBucket<T>(key)
        return bucket.setIfAbsent(value)
    }

    /**
     * 缓存基本的对象，Int、String、实体类等
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param duration 有效时间
     */
    fun <T> set(key: String, value: T, duration: Duration) {
        val batch: RBatch = CLIENT.createBatch()
        val bucket = batch.getBucket<T>(key)
        bucket.setAsync(value)
        bucket.expireAsync(duration)
        batch.execute()
    }

    /**
     * 设置缓存的set
     * 不存在返回emptySet
     * @param key 缓存的key
     * @return set对象
     */
    fun <T> setSet(key: String, value: List<T>, duration: Duration?) {
        val rSet = CLIENT.getSet<T>(key)
        rSet.addAll(value)
        if(duration != null) {
            rSet.expire(duration)
        }
    }


    /**
     * 获得缓存的set
     * 不存在返回emptySet
     * @param key 缓存的key
     * @return set对象
     */
    fun <T> getSet(key: String): HashSet<T> {
        val rSet = CLIENT.getSet<T>(key)
        return rSet.readAll().toHashSet()
    }

    /**
     * 缓存Map
     *
     * @param key     缓存的键值
     * @param dataMap 缓存的数据
     */
    fun <T> setMap(key: String, dataMap: Map<String, T>?) {
        if (dataMap != null) {
            val rMap = CLIENT.getMap<String, T>(key)
            rMap.putAll(dataMap)
        }
    }

    /**
     * 获得缓存的Map
     * 不存在返回emptyMap
     * @param key 缓存的键值
     * @return map对象
     */
    fun <T> getMap(key: String): Map<String, T> {
        val rMap = CLIENT.getMap<String, T>(key)
        return rMap.getAll(rMap.keys)
    }

    /**
     * 设置有效时间
     * @param key      Redis键
     * @param duration 超时时间
     * @return true=设置成功；false=设置失败
     */
    fun expire(key: String, duration: Duration): Boolean {
        val rBucket = CLIENT.getBucket<Any>(key)
        return rBucket.expire(duration)
    }


    /**
     * 设置有效时间
     * @param key     Redis键
     * @param seconds 超时时间 单位秒
     * @return true=设置成功；false=设置失败
     */
    fun expire(key: String, seconds: Long): Boolean {
        return expire(key, Duration.ofSeconds(seconds))
    }

    /**
     * 获得缓存的基本对象。
     * 使用方式  get<String>("key") 不存在返回null
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    fun <T> get(key: String): T? {
        val rBucket = CLIENT.getBucket<T>(key)
        return rBucket.get()
    }

    /**
     * 获得缓存的基本对象。如果为空返回默认指定值
     * 使用方式  get<String>("key", "default") 不存在key的值返回default
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    fun <T> getOrDefault(key: String, value: T): T {
        val rBucket = CLIENT.getBucket<T>(key)
        return rBucket.get() ?: value
    }

    /**
     * keys搜索
     */
    fun keys(pattern: String): Collection<String> {
        val stream = CLIENT.keys.getKeysStreamByPattern(pattern)
        return stream.collect(Collectors.toList())
    }

    /**
     * 获得key剩余过期时间
     * @param key 缓存键值
     * @return 剩余过期时间
     */
    fun  getTimeToLive(key: String): Long {
        val rBucket = CLIENT.getBucket<Any>(key)
        return rBucket.remainTimeToLive()
    }

    /**
     * 删除单个对象
     * @param key 缓存的键值
     */
    fun delete(key: String): Boolean {
        return CLIENT.getBucket<Any>(key).delete()
    }

    /**
     * 根据key值删除缓存
     * @param keys key值，可以传入多个
     */
    fun deleteKeys(vararg keys: String): Long {
        return CLIENT.keys.delete(*keys)
    }

    /**
     * 根据key匹配删除缓存
     * @param keyPattern key规则
     */
    fun deleteKeys(keyPattern: String): Long {
        return CLIENT.keys.deleteByPattern(keyPattern)
    }



    /**
     * 发布通道消息
     *
     * @param channelKey 通道key
     * @param msg        发送数据
     * @param consumer   自定义处理
     */
    fun <T> publish(channelKey: String, msg: T, consumer: Consumer<T>) {
        val topic: RTopic = CLIENT.getTopic(channelKey)
        topic.publish(msg)
        consumer.accept(msg)
    }

    fun <T> publish(channelKey: String?, msg: T) {
        val topic: RTopic = CLIENT.getTopic(channelKey)
        topic.publish(msg)
    }

    /**
     * 订阅通道接收消息
     *
     * @param channelKey 通道key
     * @param clazz      消息类型
     * @param consumer   自定义处理
     */
    fun <T> subscribe(channelKey: String, clazz: Class<T>, consumer: Consumer<T>) {
        val topic: RTopic = CLIENT.getTopic(channelKey)
        topic.addListener(clazz) { _: CharSequence, msg: T ->
            consumer.accept(
                msg
            )
        }
    }

    /**
     * 设置原子值
     * @param key   Redis键
     * @param value 值
     */
    fun setAtomicValue(key: String, value: Long) {
        val atomic: RAtomicLong = CLIENT.getAtomicLong(key)
        atomic.set(value)
    }

    /**
     * 获取原子值，不存在则设置初始值
     * @param key   Redis键
     * @param initValue 初始值
     * @param expireTime 过期时间，秒
     */
    fun getAndSetAtomicValue(key: String, initValue: Long, expireTime: Long) : Long {
        val atomic: RAtomicLong = CLIENT.getAtomicLong(key)
        return if(hasKey(key)) {
            atomic.incrementAndGet()
        } else {
            atomic.set(initValue)
            expire(key, expireTime)
            initValue
        }
    }
    /**
     * 获取原子值，不存在则设置初始值， 不过期
     * @param key   Redis键
     * @param initValue 初始值
     */
    fun getAndSetAtomicValue(key: String, initValue: Long) : Long {
        val atomic: RAtomicLong = CLIENT.getAtomicLong(key)
        return if(hasKey(key)) {
            atomic.incrementAndGet()
        } else {
            atomic.set(initValue)
            initValue
        }
    }

    /**
     * 获取原子值
     * @param key Redis键
     * @return 当前值
     */
    fun getAtomicValue(key: String): Long {
        val atomic: RAtomicLong = CLIENT.getAtomicLong(key)
        return atomic.get()
    }

    /**
     * 递增原子值
     * @param key Redis键
     * @return 当前值
     */
    fun incrAtomicValue(key: String): Long {
        val atomic: RAtomicLong = CLIENT.getAtomicLong(key)
        return atomic.incrementAndGet()
    }

    /**
     * 递减原子值
     * @param key Redis键
     * @return 当前值
     */
    fun decrAtomicValue(key: String): Long {
        val atomic: RAtomicLong = CLIENT.getAtomicLong(key)
        return atomic.decrementAndGet()
    }


}